package com.flow.framework.base.service.system.ops.impl;

import com.flow.framework.base.constant.FrameworkBaseConstant;
import com.flow.framework.base.properties.FrameworkBaseConfigProperties;
import com.flow.framework.base.properties.component.SystemOpsConfigProperties;
import com.flow.framework.base.service.system.ops.ISystemOpsSecurityService;
import com.flow.framework.common.error.SystemErrorCode;
import com.flow.framework.common.exception.CheckedException;
import com.flow.framework.common.util.collection.CollectionUtil;
import com.flow.framework.common.util.digest.SignatureUtil;
import com.flow.framework.common.util.digest.version.HmacVersion;
import com.flow.framework.common.util.verify.VerifyUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import java.util.List;
import java.util.Map;

/**
 * 系统敏感操作安全服务
 *
 * @author luoguopiao
 * @version 0.0.1
 * @date 2022/12/11
 */
@Slf4j
@RequiredArgsConstructor
public class SystemOpsSecurityServiceImpl implements ISystemOpsSecurityService {

    private final FrameworkBaseConfigProperties frameworkBaseConfigProperties;

    /**
     * @inheritDoc
     */
    @Override
    public boolean isUriMatch(String contextPath, String method, String uri) {
        if (null == contextPath) {
            log.error("param error. context path can't null");
            throw new CheckedException(SystemErrorCode.PARAMS_ERROR);
        }

        return (contextPath + FrameworkBaseConstant.NO_CONTEXT_PATH_HEALTH_CHECK_URI).equalsIgnoreCase(uri)
                || (contextPath + FrameworkBaseConstant.NO_CONTEXT_PATH_BEFORE_SHUTDOWN_URI).equalsIgnoreCase(uri)
                || (contextPath + FrameworkBaseConstant.NO_CONTEXT_PATH_MANUAL_ONLINE_URI).equalsIgnoreCase(uri);
    }

    /**
     * @inheritDoc
     */
    @Override
    public boolean authentication(Map<String, List<String>> headers) {
        SystemOpsConfigProperties systemOps = frameworkBaseConfigProperties.getSystemOps();
        if (VerifyUtil.isEmpty(systemOps)) {
            log.error("unauthorized error, config is null");
            return false;
        }
        String accessHeaderKey = systemOps.getAccessHeaderKey();
        List<String> headerSecrets = headers.get(accessHeaderKey);
        if (VerifyUtil.isEmpty(headerSecrets)) {
            log.error("unauthorized error.");
            return false;
        }
        String accessSecret = CollectionUtil.getFirstElement(headerSecrets);
        boolean signVerify = systemOps.isSignVerify();
        if (signVerify) {
            String requestTimeHeaderKey = systemOps.getRequestTimeHeaderKey();
            if (VerifyUtil.isEmpty(requestTimeHeaderKey)) {
                log.error("unauthorized error, request time header key is empty.");
                return false;
            }

            List<String> requestTimeHeaders = headers.get(requestTimeHeaderKey);
            if (VerifyUtil.isEmpty(requestTimeHeaders)) {
                log.error("unauthorized error, request time header is empty.");
                return false;
            }
            String requestTime = CollectionUtil.getFirstElement(requestTimeHeaders);
            if (VerifyUtil.isEmpty(requestTime)) {
                log.error("unauthorized error, request time is empty.");
                return false;
            }
            try {
                long requestTimeLong = Long.parseLong(requestTime);
                if (System.currentTimeMillis() - requestTimeLong > systemOps.getDuration()) {
                    log.error("unauthorized error, sign expire. request time: {}", requestTime);
                    return false;
                }

                //把timestamp+密钥当做签名的秘钥对空数组进行签名
                String key = requestTime + systemOps.getAccessSecret();
                if (!SignatureUtil.hmacVerifyByContent(HmacVersion.HMV1, null, key, accessSecret)) {
                    log.error("unauthorized error, sign error. request time: {}, access sign: {}", requestTime, accessSecret);
                    return false;
                }
                return true;
            } catch (Exception e) {
                log.error("authentication error.", e);
            }
            return false;
        }
        if (!VerifyUtil.isEmpty(accessSecret) && accessSecret.equals(systemOps.getAccessSecret())) {
            return true;
        }
        log.error("unauthorized error");
        return false;
    }
}